package com.yvan.periscope.util;

import com.yvan.periscope.stat.JvmStat;

import java.lang.management.*;
import java.util.List;

/**
 * 作者： lzw<br/>
 * 创建时间：2018-08-27 17:33 <br/>
 */
public final class JvmUtils {

    private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean();

    private static final List<GarbageCollectorMXBean> GARBAGE_COLLECTOR_MX_BEAN_LIST = ManagementFactory.getGarbageCollectorMXBeans();

    private static final MemoryMXBean MEMORY_MX_BEAN = ManagementFactory.getMemoryMXBean();

    private static final List<MemoryPoolMXBean> MEMORY_POOL_MX_BEAN_LIST = ManagementFactory.getMemoryPoolMXBeans();

    private static final JvmStat JVM_STAT = new JvmStat();

    /**
     * 读取JVM状信息
     */
    public static synchronized JvmStat getJvmStat() {
        // Thread
        if (THREAD_MX_BEAN != null) {
            JVM_STAT.setThreadCount(THREAD_MX_BEAN.getThreadCount());
            long[] threadIds = THREAD_MX_BEAN.findDeadlockedThreads();
            JVM_STAT.setThreadDeadlocks(threadIds == null ? 0 : threadIds.length);

            JVM_STAT.setThreadBlockedCount(getThreadCount(Thread.State.BLOCKED));
        }
        // GC
        if (GARBAGE_COLLECTOR_MX_BEAN_LIST != null) {
            for (final GarbageCollectorMXBean gc : GARBAGE_COLLECTOR_MX_BEAN_LIST) {
                if (gc.getName() == null) {
                    continue;
                }
                long countSum = gc.getCollectionCount();
                long timeSum = gc.getCollectionTime();
                switch (gc.getName().toLowerCase().trim()) {
                    case "ps scavenge":
                        // 计算增量
                        if (JVM_STAT.getPsScavengeCount() < 0 || JVM_STAT.getPsScavengeCountSum() < 0) {
                            JVM_STAT.setPsScavengeCount(countSum);
                        } else {
                            JVM_STAT.setPsScavengeCount(countSum - JVM_STAT.getPsScavengeCountSum());
                        }
                        if (JVM_STAT.getPsScavengeTime() < 0 || JVM_STAT.getPsScavengeTimeSum() < 0) {
                            JVM_STAT.setPsScavengeTime(timeSum);
                        } else {
                            JVM_STAT.setPsScavengeTime(timeSum - JVM_STAT.getPsScavengeTimeSum());
                        }
                        // sum
                        JVM_STAT.setPsScavengeCountSum(countSum);
                        JVM_STAT.setPsScavengeTimeSum(timeSum);
                        break;
                    case "ps marksweep":
                        // 计算增量
                        if (JVM_STAT.getPsMarkSweepCount() < 0 || JVM_STAT.getPsMarkSweepCountSum() < 0) {
                            JVM_STAT.setPsMarkSweepCount(countSum);
                        } else {
                            JVM_STAT.setPsMarkSweepCount(countSum - JVM_STAT.getPsMarkSweepCountSum());
                        }
                        if (JVM_STAT.getPsMarkSweepTime() < 0 || JVM_STAT.getPsMarkSweepTimeSum() < 0) {
                            JVM_STAT.setPsMarkSweepTime(timeSum);
                        } else {
                            JVM_STAT.setPsMarkSweepTime(timeSum - JVM_STAT.getPsMarkSweepTimeSum());
                        }
                        // sum
                        JVM_STAT.setPsMarkSweepCountSum(countSum);
                        JVM_STAT.setPsMarkSweepTimeSum(timeSum);
                        break;
                }
            }
        }
        // Memory
        if (MEMORY_MX_BEAN != null) {
            JVM_STAT.setMemoryHeapMax(MEMORY_MX_BEAN.getHeapMemoryUsage().getMax());
            JVM_STAT.setMemoryHeapUsed(MEMORY_MX_BEAN.getHeapMemoryUsage().getUsed());

            JVM_STAT.setMemoryNonHeapMax(MEMORY_MX_BEAN.getNonHeapMemoryUsage().getMax());
            JVM_STAT.setMemoryNonHeapUsed(MEMORY_MX_BEAN.getNonHeapMemoryUsage().getUsed());

            JVM_STAT.setMemoryTotalMax(MEMORY_MX_BEAN.getHeapMemoryUsage().getMax() + MEMORY_MX_BEAN.getNonHeapMemoryUsage().getMax());
            JVM_STAT.setMemoryTotalUsed(MEMORY_MX_BEAN.getHeapMemoryUsage().getUsed() + MEMORY_MX_BEAN.getNonHeapMemoryUsage().getUsed());
        }
        if (MEMORY_POOL_MX_BEAN_LIST != null) {
            for (MemoryPoolMXBean pool : MEMORY_POOL_MX_BEAN_LIST) {
                if (pool.getName() == null) {
                    continue;
                }
                switch (pool.getName().toLowerCase().trim()) {
                    case "ps eden space":
                        JVM_STAT.setMemoryPoolsPsEdenSpaceMax(pool.getUsage().getMax());
                        JVM_STAT.setMemoryPoolsPsEdenSpaceUsed(pool.getUsage().getUsed());
                        break;
                    case "ps old gen":
                        JVM_STAT.setMemoryPoolsPsOldGenMax(pool.getUsage().getMax());
                        JVM_STAT.setMemoryPoolsPsOldGenUsed(pool.getUsage().getUsed());
                        break;
                }
            }
        }
        return JVM_STAT;
    }

    private static int getThreadCount(final Thread.State state) {
        final ThreadInfo[] allThreads = THREAD_MX_BEAN.getThreadInfo(THREAD_MX_BEAN.getAllThreadIds(), 0);
        int count = 0;
        for (ThreadInfo info : allThreads) {
            if (info != null && info.getThreadState() == state) {
                count++;
            }
        }
        return count;
    }
}
